#include"server.hpp"
#include"socket.hpp"
#include"buffer.hpp"
#include"Any.hpp"

typedef enum{
    DISCONNECTED,//连接关闭状态
    CONNECTING,//连接建立成功--待处理状态
    CONNECTED,//连接建立完成，各种设置已完成，可以通信的状态
    DISCONNECTING//待关闭状态
}Conn_Status;

class Connection;
using PtrConnection=std::shared_ptr<Connection>;
using ConnectedCallback=std::function<void(const PtrConnection&)>;
using MessageCallback=std::function<void(const PtrConnection&,Buffer*)>;//把缓冲区给他，他就可以对缓冲区里的内容进行业务处理了
using ClosedCallback=std::function<void(const PtrConnection&)>;
using AnyEventCallback=std::function<void(const PtrConnection&)>;

class Connection:public std::enable_shared_from_this<Connection>{
private:
    /*五个channel的事件回调*/
    /*描述符触发可读事件后调用的函数，接收socket数据放到接收缓冲区中，然后调用_message_callback进行处理*/
    void ReadHandler(){
        //1.接收socket的数据，放到缓冲区
        char buffer[65536];
        ssize_t ret=_socket.NonBlockRecv(buffer,sizeof(buffer)-1);
        if(ret<0)   return ShutdownInLoop();//出错了，不能直接关闭连接
        //如果ret返回0，说明没有读取到数据，而不是连接断开了，连接断开返回的是-1
        //将数据放入输入缓冲区,写入之后顺便将写偏移往后移动
        _in_buffer.WriteAndPush(buffer,ret);
        //2.调用_message_callback进行业务处理
        if(_in_buffer.ReadAlbeSize()) return _message_callback(shared_from_this(),&_in_buffer);

    }
    /*描述符可写事件触发后调用的函数，将发送缓冲区中的数据进行发送*/
    void WriteHandler(){
        ssize_t ret=_socket.NonBlockSend(_out_buffer.ReadPosition(),_out_buffer.ReadAlbeSize());
        if(ret<0){
            //发送错误，就该关闭连接了
            if(_in_buffer.ReadAlbeSize()){
                _message_callback(shared_from_this(),&_in_buffer);
            }
            return ReleaseInLoop();//在这里就是实际的释放操作了：ReadHandler那里如果出错了就去看一下，发送缓冲区还有没有数据要发送（就是调用ShutdownInLoop,这里会启动可写事件监控）-->就会跑到WriteHandler，如果没有数据，就关闭连接了，而如果是写出现了错误，就直接判断，判断没有就之际关闭了
        }
        _out_buffer.MoveReadOffset(ret);//将读向后偏移
        if(_out_buffer.ReadAlbeSize()==0){//如果发送缓冲区的数据为0，就关闭写事件监控，因为他有可能造成不断去触发写事件监控，但是又没东西可写的问题
            _channel.DisableWrite();
            //如果当前是连接待关闭状态，则有数据，发送完数据释放连接，没有数据则直接释放
            if(_status==DISCONNECTING)
            return ReleaseInLoop();
        }
    }
    /*描述符触发挂断事件*/
    void CloseHandler(){
        /*一旦连接挂断了，套接字就什么都干不了了，因此又数据待处理就处理一下，完毕关闭连接*/
        if(_in_buffer.ReadAlbeSize()>0)
            return _message_callback(shared_from_this(),&_in_buffer);
        return ReleaseInLoop();
    }
    /*描述符触发错误事件*/
    void ErrorHandler(){return CloseHandler();}
    /*描述符触发任意事件：1.刷新连接的活跃度--延迟定时销毁任务；2.调用组件使用者的任意事件回调*/
    void EventHandler(){
        if(_enable_inactive_release) return _loop->TimerRefresh(_conn_id);
        if(_event_callback) return _event_callback(shared_from_this());
    }

    //连接获取之后，对于所处的状态下进行各种设置（启动读监控，调用回调函数）
    void EstablishedInLoop(){
        //1.修改连接状态---一般调用这个函数是用来建立连接，所有是从半连接状态过来的，如果不是，那就不能调用这个函数
        assert(_status==CONNECTING);//当前的状态一定是上层的半连接状态，也就是还没有连接完成
        _status=CONNECTED;
        //2.启动读事件监控--因为这一步是不能放在构造函数里完成的，因为启动读事件监控后就有可能立即触发读事件，如果此时启动了非活跃连接销毁
        //他就会去刷新我们的活跃度，延迟我们的销毁任务的执行，如果放在构造函数里的话，此时还没有添加定时任务呢，此时他去找这个定时任务肯定找不着，逻辑是有问题的
        _channel.EnableRead();
        //3.调用回调函数
        if(_connected_callback) _connected_callback(shared_from_this());
    }
    //实际的释放接口
    void ReleaseInLoop(){
        //1.修改连接状态，将其置为DISCONNECTED
        _status=DISCONNECTED;
        //2.移除连接的事件监控
        _channel.Remove();
        //3.关闭描述符
        _socket.Close();
        //4.如果当前定时器队列中还有定时销毁任务，则取消任务
        if(_loop->HasTimer(_conn_id)) CancelInactiveReleaseInLoop();
        //5.调用关闭回调函数，避免先移除服务器管理的连接信息导致Connection被释放，此时如果还去处理会出错，所以先调用用户的
        if(_closed_callback) _closed_callback(shared_from_this());
        //移除服务器内部管理的连接信息
        if(_server_closed_callback) _server_closed_callback(shared_from_this());
    }
    /*这个接口并不是实际的发送接口，而只是把数据放到了发送缓冲区，启动了可写事件监控*/
    void SendInLoop(Buffer buffer){//这里也不适用引用，直接复制一份过来（防止在Send里面构建的Buffer也被释放）
        if(_status==DISCONNECTED) return;
        _out_buffer.WriteBufferAndPush(buffer);
        if(_channel.WriteAble()==false)
            _channel.EnableWrite();
    }
    /*这个关闭操作并非实际的连接释放操作，需要判断还有没有数据待处理，待发送*/
    void ShutdownInLoop(){
        _status=DISCONNECTING;//设置连接为半关闭
        if(_in_buffer.ReadAlbeSize()>0){
            //如果还有数据就处理一下
            if(_message_callback) _message_callback(shared_from_this(),&_in_buffer);
        }  
        if(_out_buffer.ReadAlbeSize()>0){
            if(_channel.WriteAble()==false)
                _channel.EnableWrite();//如果还有数据没有发送，就先发送
        }    
        if(_out_buffer.ReadAlbeSize()==0)
            ReleaseInLoop();  
    }
    void EnableInactiveReleaseInLoop(int sec){
        //1.将判断标志_enable_inactive_release置为true
        _enable_inactive_release=true;
        //2.如果房前定时销毁任务已经存在，那就刷新一下延迟
        if(_loop->HasTimer(_conn_id))
            return _loop->TimerRefresh(_conn_id);
        //3.如果不存在定时销毁任务，则新增
        _loop->TimerAdd(_conn_id,sec,std::bind(&Connection::ReleaseInLoop,this));
    }
    void CancelInactiveReleaseInLoop(){
        _enable_inactive_release=false;
        if(_loop->HasTimer(_conn_id))
            _loop->TimerCancel(_conn_id);
    }
    void UpgradeInLoop(const Any&context,const ConnectedCallback& conn,const MessageCallback& msg,
        const ClosedCallback& closed,const AnyEventCallback& any){
            _context=context;
            _connected_callback=conn;
            _message_callback=msg;
            _closed_callback=closed;
            _event_callback=any;
        }
public:
    Connection(EventLoop* loop,uint64_t conn_id,int sockfd)
    :_conn_id(conn_id),_sockfd(sockfd),_loop(loop),_channel(_sockfd,_loop),
    _socket(sockfd),_enable_inactive_release(false),_status(CONNECTING)
    {
        _channel.SetCloseCallback(std::bind(&Connection::CloseHandler,this));
        _channel.SetEventCallback(std::bind(&Connection::EventHandler,this));
        _channel.SetReadCallback(std::bind(&Connection::ReadHandler,this));
        _channel.SetWriteCallback(std::bind(&Connection::WriteHandler,this));
        _channel.SetErrorCallback(std::bind(&Connection::ErrorHandler,this));
    }
    ~Connection(){DLOG("RELEASE CONNECTION:%p",this);}
    int Fd(){return _sockfd;}
    int Id(){return _conn_id;}
    //判断是否处于CONNECTED状态
    bool Connected(){return (_status=CONNECTED);}
    //设置上下文
    void SetContext(const Any&context){_context=context;}
    //获取上下文
    Any* GetContext(){return &_context;}

    void SetConnectedCallback(const ConnectedCallback&cb){_connected_callback=cb;}
    void SetMessageCallback(const MessageCallback&cb){_message_callback=cb;}
    void SetClosedCallback(const ClosedCallback&cb){_closed_callback=cb;}
    void SetAnyEventCallback(const AnyEventCallback&cb){_event_callback=cb;}
    void SetSrvClosedCallback(const ClosedCallback&cb){_server_closed_callback=cb;}
    //连接建立就绪后，进行channel回调设置，启动读监控，调用_connected_callback
    void Establish(){
        _loop->RunLoop(std::bind(&Connection::EstablishedInLoop,this));
    }
    //发送数据，将数据放到发送缓冲区，启动写事件监控
    void Send(const char*data,size_t len){
        //注意：外界传入的data，可能是一个临时的空间，在这里知识把发送操作压入到任务池里面，他有可能并没有给立即执行
        //因此有可能执行的时候，data指向的空间有可能已经被释放了。因此在这里给他重新构建一个对象buffer，把他保存起来
        Buffer buffer;
        buffer.WriteAndPush(data,len);
        _loop->RunLoop(std::bind(&Connection::SendInLoop,this,buffer));
    }
    //提供给组件使用者的关闭接口--并不立即关闭，需要判断还有没有数据待处理
    void Shutdown(){
        _loop->RunLoop(std::bind(&Connection::ShutdownInLoop,this));
    }
    //启动非活跃销毁，并定义多长时间无通信就是非活跃，添加定时任务
    void EnableInactiveRelease(int sec){
        _loop->RunLoop(std::bind(&Connection::EnableInactiveReleaseInLoop,this,sec));
    }
    //取消非活跃
    void CancelInactiveRelease(){
        _loop->RunLoop(std::bind(&Connection::CancelInactiveReleaseInLoop,this));
    }
    //协议切换--重置上下文以及阶段性回调处理函数--这个接口不是线程安全的，本次数据处理完成之后，想要切换/升级协议，是需要被立即执行的
    //他是必须在线程当中被调用的，因为这里只是把切换协议任务放到队列里面，此时还没有被执行，如果此时前面有数据要处理，此时还是用的原协议（本意是想用新的协议来处理这个数据的）
    //---这个接口必须在EventLoop线程中立即执行---防备新的实践触发后，处理的时候，切换任务还没有被执行--会导致数据使用原协议处理
    void Upgrade(const Any&context,const ConnectedCallback& conn,const MessageCallback& msg,
        const ClosedCallback& closed,const AnyEventCallback& any){
            _loop->AssertInLoop();//添加一个接口断言：如果是在本线程里调用，则可以执行--否则调用不了此Upgrade接口
            _loop->RunLoop(std::bind(&Connection::UpgradeInLoop,this,context,conn,msg,closed,any));
        }


private:
    uint64_t _conn_id;// 连接的唯一ID，便于连接的管理和查找
    //uint64_t _timer_id;//定时器ID，必须是唯一的，为了简化操作，使用conn_id作为定时器ID
    int _sockfd;//连接关联的文件描述符号
    Conn_Status _status;//用来记录当前连接的状态
    bool _enable_inactive_release;//连接是否启动非活跃销毁的判断标志，默认为false
    Socket _socket;//套接字操作管理
    EventLoop *_loop;//连接所关联的EventLoop
    Channel _channel;//连接的事件管理
    
    Buffer _in_buffer;//输入缓冲区--存放从socket中读取到的数据
    Buffer _out_buffer;//输出缓冲区--存放要发送给对端的数据
    Any _context;// 请求的接收处理上下文

    //阶段回调函数管理--提供给外界，来进行连接的某种操作的
    //这四个回调函数，是让服务器模块使用者来设置的
    ConnectedCallback _connected_callback;
    MessageCallback _message_callback;
    ClosedCallback _closed_callback;
    AnyEventCallback _event_callback;
    //组件内连接关闭回调--组件内设置的，因为服务器组件内会把所有连接管理起来，一旦某个连接要关闭，就应该从管理的地方移除掉自己的信息
    ClosedCallback _server_closed_callback;
};